﻿using System;
using System.ComponentModel;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;

namespace ICodeShare.UI.Controls
{
    /// <summary>
    /// 一个带阴影效果的边框
    /// A border that also shows a drop shadow.
    /// </summary>
    public class DropShadowBorder : ContentControl
    {
        #region Fields

        /// <summary>
        /// Stores the drop shadow translate transform.
        /// </summary>
        private TranslateTransform dropShadowTranslate;

        /// <summary>
        /// The top out gradient stop.
        /// </summary>
        private GradientStop shadowOuterStop1;

        /// <summary>
        /// The bottom outer gradient stop.
        /// </summary>
        private GradientStop shadowOuterStop2;

        /// <summary>
        /// Stores the top gradient stop.
        /// </summary>
        private GradientStop shadowVertical1;

        /// <summary>
        /// Stores the bottom gradient stop.
        /// </summary>
        private GradientStop shadowVertical2;

        /// <summary>
        /// Stores the left gradient stop.
        /// </summary>
        private GradientStop shadowHorizontal1;

        /// <summary>
        /// Stores the right gradient stop.
        /// </summary>
        private GradientStop shadowHorizontal2;

        #endregion

        #region Constructors

        /// <summary>
        /// Initialises a new instance of the <see cref="DropShadowBorder"/> class.
        /// </summary>
        public DropShadowBorder()
        {
            this.DefaultStyleKey = typeof(DropShadowBorder);
            this.SizeChanged += new SizeChangedEventHandler(this.OnSizeChanged);
        }

        #endregion

        #region Dependency Properties

        #region DropShadowColor 阴影效果颜色

        /// <summary>
        /// 获取或设置阴影效果颜色
        /// Gets or sets the drop shadow colour.
        /// </summary>
        [Category("Appearance"), Description("the drop shadow color.")]
        public Color DropShadowColor
        {
            get { return (Color)this.GetValue(DropShadowColorProperty); }
            set { this.SetValue(DropShadowColorProperty, value); }
        }

        /// <summary>
        /// The drop shadow colour property.
        /// </summary>
        public static readonly DependencyProperty DropShadowColorProperty = DependencyProperty.Register(
            "DropShadowColor",
            typeof(Color),
            typeof(DropShadowBorder),
            new PropertyMetadata(Colors.Black, OnDropShadowColorChanged));

        /// <summary>
        /// Updates the drop shadow.
        /// </summary>
        /// <param name="dependencyObject">The drop shadow border.</param>
        /// <param name="args">Dependency Property Changed Event Args</param>
        private static void OnDropShadowColorChanged(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs args)
        {
            DropShadowBorder dropShadowBorder = (DropShadowBorder)dependencyObject;
            dropShadowBorder.UpdateDropShadowColor((Color)args.NewValue);
        }

        #endregion

        #region DropShadowOpacity 阴影效果透明度

        /// <summary>
        /// 获取或设置阴影效果透明度
        /// Gets or sets the drop shadow opacity.
        /// </summary>
        [Category("Appearance"), Description("The drop shadow opacity.")]
        public double DropShadowOpacity
        {
            get { return (double)this.GetValue(DropShadowOpacityProperty); }
            set { this.SetValue(DropShadowOpacityProperty, value); }
        }

        /// <summary>
        /// The drop shadow opacity property.
        /// </summary>
        public static readonly DependencyProperty DropShadowOpacityProperty = DependencyProperty.Register(
            "DropShadowOpacity",
            typeof(double),
            typeof(DropShadowBorder),
            new PropertyMetadata(1.0));

        #endregion

        #region ShadowDepth 阴影效果偏移

        /// <summary>
        /// 获取或设置阴影效果偏移
        /// Gets or sets the drop shadow distance.
        /// </summary>
        [Category("Appearance"), Description("The drop shadow distance.")]
        public double ShadowDepth
        {
            get { return (double)this.GetValue(ShadowDepthProperty); }
            set { this.SetValue(ShadowDepthProperty, value); }
        }

        /// <summary>
        /// The drop shadow distance property.
        /// </summary>
        public static readonly DependencyProperty ShadowDepthProperty = DependencyProperty.Register(
            "ShadowDepth",
            typeof(double),
            typeof(DropShadowBorder),
            new PropertyMetadata(5.0, OnShadowDepthChanged));

        /// <summary>
        /// Updates the drop shadow.
        /// </summary>
        /// <param name="dependencyObject">The drop shadow border.</param>
        /// <param name="args">Dependency Property Changed Event Args</param>
        private static void OnShadowDepthChanged(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs args)
        {
            DropShadowBorder dropShadowBorder = (DropShadowBorder)dependencyObject;
            dropShadowBorder.UpdateDropShadowPosition();
        }

        #endregion

        #region ShadowDirection 阴影效果角度

        /// <summary>
        /// 获取或设置阴影效果角度
        /// Gets or sets the drop shadow angle.
        /// </summary>
        [Category("Appearance"), Description("The drop shadow angle.")]
        public double ShadowDirection
        {
            get { return (double)this.GetValue(ShadowDirectionProperty); }
            set { this.SetValue(ShadowDirectionProperty, value); }
        }

        /// <summary>
        /// The drop shadow angle property.
        /// </summary>
        public static readonly DependencyProperty ShadowDirectionProperty = DependencyProperty.Register(
            "ShadowDirection",
            typeof(double),
            typeof(DropShadowBorder),
            new PropertyMetadata(45.0, OnShadowDirectionChanged));

        /// <summary>
        /// Updates the drop shadow.
        /// </summary>
        /// <param name="dependencyObject">The drop shadow border.</param>
        /// <param name="args">Dependency Property Changed Event Args</param>
        private static void OnShadowDirectionChanged(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs args)
        {
            DropShadowBorder dropShadowBorder = (DropShadowBorder)dependencyObject;
            dropShadowBorder.UpdateDropShadowPosition();
        }

        #endregion

        #region BlurRadius 阴影效果扩散系数

        /// <summary>
        /// 获取或设置阴影效果扩散系数
        /// Gets or sets the drop shadow spread.
        /// </summary>
        [Category("Appearance"), Description("The drop shadow spread.")]
        public double BlurRadius
        {
            get { return (double)this.GetValue(BlurRadiusProperty); }
            set { this.SetValue(BlurRadiusProperty, value); }
        }

        /// <summary>
        /// 
        /// The drop shadow spread property.
        /// </summary>
        public static readonly DependencyProperty BlurRadiusProperty = DependencyProperty.Register(
            "BlurRadius",
            typeof(double),
            typeof(DropShadowBorder),
            new PropertyMetadata(2.0, OnBlurRadiusChanged));

        /// <summary>
        /// Updates the drop shadow.
        /// </summary>
        /// <param name="dependencyObject">The drop shadow border.</param>
        /// <param name="args">Dependency Property Changed Event Args</param>
        private static void OnBlurRadiusChanged(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs args)
        {
            DropShadowBorder dropShadowBorder = (DropShadowBorder)dependencyObject;
            dropShadowBorder.UpdateStops(new Size(dropShadowBorder.ActualWidth, dropShadowBorder.ActualHeight));
        }

        #endregion

        #region CornerRadius 边框圆角

        /// <summary>
        /// 获取或设置阴影效果透明度
        /// Gets or sets the border corner radius.
        /// This is a thickness, as there is a problem parsing CornerRadius types.
        /// </summary>
        [Category("Appearance"), Description("Sets the corner radius on the border.")]
        public CornerRadius CornerRadius
        {
            get { return (CornerRadius)this.GetValue(CornerRadiusProperty); }
            set { this.SetValue(CornerRadiusProperty, value); }
        }

        /// <summary>
        /// The corner radius property.
        /// </summary>
        public static readonly DependencyProperty CornerRadiusProperty = DependencyProperty.Register(
            "CornerRadius",
            typeof(CornerRadius),
            typeof(DropShadowBorder),
            new PropertyMetadata(new CornerRadius(0), OnCornerRadiusChanged));

        /// <summary>
        /// Updates the drop shadow.
        /// </summary>
        /// <param name="dependencyObject">The drop shadow border.</param>
        /// <param name="args">Dependency Property Changed Event Args</param>
        private static void OnCornerRadiusChanged(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs args)
        {
            DropShadowBorder dropShadowBorder = (DropShadowBorder)dependencyObject;
            CornerRadius newCornerRadius = (CornerRadius)args.NewValue;
            CornerRadius shadowCornerRadius = new CornerRadius(
                   Math.Abs(newCornerRadius.TopLeft * 1.5),
                   Math.Abs(newCornerRadius.TopRight * 1.5),
                   Math.Abs(newCornerRadius.BottomRight * 1.5),
                   Math.Abs(newCornerRadius.BottomLeft * 1.5));

            dropShadowBorder.ShadowCornerRadius=shadowCornerRadius;
        }



        #endregion

        #region ShadowCornerRadius 阴影效果边框圆角

        /// <summary>
        /// 获取阴影效果边框圆角
        /// </summary>
        public CornerRadius ShadowCornerRadius
        {
            get { return (CornerRadius)this.GetValue(ShadowCornerRadiusProperty); }
            private set { this.SetValue(ShadowCornerRadiusPropertyKey, value); }
        }

        /// <summary>
        /// The shadow corner radius property.
        /// </summary>
        private static readonly DependencyPropertyKey ShadowCornerRadiusPropertyKey = DependencyProperty.RegisterReadOnly(
            "ShadowCornerRadius",
            typeof(CornerRadius),
            typeof(DropShadowBorder),
            new PropertyMetadata(new CornerRadius(0)));

        public static readonly DependencyProperty ShadowCornerRadiusProperty = ShadowCornerRadiusPropertyKey.DependencyProperty;

        #endregion

        #region ClipContent 是否裁剪内容

        /// <summary>
        /// 获取或设置是否裁剪内容
        /// Gets or sets a value indicating whether the content is clipped.
        /// </summary>
        [Category("Appearance"), Description("Sets whether the content is clipped or not.")]
        public bool ClipContent
        {
            get { return (bool)this.GetValue(ClipContentProperty); }
            set { this.SetValue(ClipContentProperty, value); }
        }

        /// <summary>
        /// 是否裁剪内容属性
        /// The clip content property.
        /// </summary>
        public static readonly DependencyProperty ClipContentProperty = DependencyProperty.Register(
            "ClipContent",
            typeof(bool),
            typeof(DropShadowBorder),
            new PropertyMetadata(true));

        #endregion

        #endregion

        /// <summary>
        /// Gets a point offset by a distance and angle (in degrees).
        /// </summary>
        /// <param name="angle">The angle in degrees.</param>
        /// <param name="distance">The distance.</param>
        /// <returns>The offset point.</returns>
        public static Point GetOffset(double angle, double distance)
        {
            double x = Math.Cos(DegreesToRadians(angle)) * distance;
            double y = Math.Tan(DegreesToRadians(angle)) * x;
            return new Point(x, y);
        }

        /// <summary>
        /// Converts degrees into radians.
        /// </summary>
        /// <param name="degrees">The degree value.</param>
        /// <returns>The degrees as radians.</returns>
        public static double DegreesToRadians(double degrees)
        {
            return degrees * (Math.PI / 180);
        }

        /// <summary>
        /// Gets the parts from the template.
        /// </summary>
        public override void OnApplyTemplate()
        {
            base.OnApplyTemplate();
            this.dropShadowTranslate = (TranslateTransform)this.GetTemplateChild("PART_DropShadowTranslateTransform");
            this.shadowOuterStop1 = (GradientStop)this.GetTemplateChild("PART_ShadowOuterStop1");
            this.shadowOuterStop2 = (GradientStop)this.GetTemplateChild("PART_ShadowOuterStop2");
            this.shadowVertical1 = (GradientStop)this.GetTemplateChild("PART_ShadowVertical1");
            this.shadowVertical2 = (GradientStop)this.GetTemplateChild("PART_ShadowVertical2");
            this.shadowHorizontal1 = (GradientStop)this.GetTemplateChild("PART_ShadowHorizontal1");
            this.shadowHorizontal2 = (GradientStop)this.GetTemplateChild("PART_ShadowHorizontal2");

            this.UpdateDropShadowPosition();
            this.UpdateStops(new Size(this.ActualWidth, this.ActualHeight));
        }

        /// <summary>
        /// Updates the drop shadow.
        /// </summary>
        internal void UpdateDropShadowPosition()
        {
            if (this.dropShadowTranslate != null)
            {
                Point offset = GetOffset(this.ShadowDirection, this.ShadowDepth+1);

                this.dropShadowTranslate.X = offset.X;
                this.dropShadowTranslate.Y = -offset.Y;
            }
        }

        /// <summary>
        /// Updates the gradient stops.
        /// </summary>
        /// <param name="size">The size of the control.</param>
        internal void UpdateStops(Size size)
        {
            if (size.Width > 0 && size.Height > 0)
            {
                if (this.shadowHorizontal1 != null)
                {
                    this.shadowHorizontal1.Offset = this.BlurRadius / (size.Width + (this.BlurRadius * 2));
                }

                if (this.shadowHorizontal2 != null)
                {
                    this.shadowHorizontal2.Offset = 1 - (this.BlurRadius / (size.Width + (this.BlurRadius * 2)));
                }

                if (this.shadowVertical1 != null)
                {
                    this.shadowVertical1.Offset = this.BlurRadius / (size.Height + (this.BlurRadius * 2));
                }

                if (this.shadowVertical2 != null)
                {
                    this.shadowVertical2.Offset = 1 - (this.BlurRadius / (size.Height + (this.BlurRadius * 2)));
                }
            }
        }

        /// <summary>
        /// Updates the drop shadow colour.
        /// </summary>
        /// <param name="color">The new colour.</param>
        internal void UpdateDropShadowColor(Color color)
        {
            if (this.shadowVertical1 != null)
            {
                this.shadowVertical1.Color = color;
            }

            if (this.shadowVertical2 != null)
            {
                this.shadowVertical2.Color = color;
            }

            if (this.shadowOuterStop1 != null)
            {
                this.shadowOuterStop1.Color = Color.FromArgb(
                    0,
                    color.R,
                    color.G,
                    color.B);
            }

            if (this.shadowOuterStop2 != null)
            {
                this.shadowOuterStop2.Color = Color.FromArgb(
                    0,
                    color.R,
                    color.G,
                    color.B);
            }
        }

        /// <summary>
        /// Updates the stops.
        /// </summary>
        /// <param name="sender">The drop shadow border.</param>
        /// <param name="e">Size changed event args.</param>
        private void OnSizeChanged(object sender, SizeChangedEventArgs e)
        {
            this.UpdateStops(e.NewSize);
        }
    }
}